home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Turnbull China Bikeride
/
Turnbull China Bikeride - Disc 2.iso
/
BARNET
/
COMPILER
/
SATHER
/
!Sather
/
Library
/
Math
/
sa
/
mat
< prev
next >
Wrap
Text File
|
1996-07-18
|
40KB
|
1,290 lines
---------------------------> Sather 1.1 source file <--------------------------
-- Copyright (C) International Computer Science Institute, 1994. COPYRIGHT --
-- NOTICE: This code is provided "AS IS" WITHOUT ANY WARRANTY and is subject --
-- to the terms of the SATHER LIBRARY GENERAL PUBLIC LICENSE contained in --
-- the file "Doc/License" of the Sather distribution. The license is also --
-- available from ICSI, 1947 Center St., Suite 600, Berkeley CA 94704, USA. --
--------> Please email comments to "sather-bugs@icsi.berkeley.edu". <----------
-- Author: Kennel <mbk@icsi.berkeley.edu>
-------------------------------------------------------------------
class MAT{ET<$NFE{ET},VT<$VEC{ET,VT}} < $MAT{ET,VT,MAT{ET,VT}} is
-- This is a purely Sather implementation of the generic dense
-- concrete matrix class, with *COLUMN-MAJOR* (fortran-style)
-- layout. First index changes most rapidly in stepping thru storage.
-- This was written before iterator optimizations - a more natural
-- style using the ind! and other iterators which are now built-in
-- could be adopted. At the time it was written, the compiler
-- did not dod these optimizations.
-- Original note from Matt:n
-- Because iters are not yet optimized or inlined in the compiler I
-- will become a compromise for speed and write out operations with
-- boring conventional loops. Implementation will tend to use extra
-- index variabless to avoid integer multiplication inside the loop.
-- This may rely on assumed column major ordering. Also I will
-- attempt to use local variables and not attributes in loops.
private include AREF{ET} aclear->aclear,aget->aget,aset->aset;
-- makes those named features public, others stay private.
readonly attr nr:INT; -- number of rows
readonly attr nc:INT; -- number of columns
-- built in feature inherited from AREF{*}, asize = nr*nc.
size: INT is return asize end;
size1:INT is return nr end;
size2:INT is return nc end;
element_zero:ET is return ET::zero; end;
element_one:ET is return ET::one; end;
-- These may need be redefined for MATCPX et al. This is a hack. Zero
-- and one should really be features in in ET < $RING_ELT, where
-- $RING_ELT gives additive and multiplicative identity.
is_same_shape(arg:SAME):BOOL is
-- useful in preconditions. Is arg dimensioned the same as self?
if void(arg) or void(self) then
return false;
elsif arg.asize /= asize or arg.nr /= nr then
return false;
else return true; end;
end;
is_same_shape_trans(arg:SAME):BOOL is
-- does arg^T conform to 'self'?
if void(arg) or void(self) then
return false;
elsif arg.asize /= asize or arg.nr /= nc then
return false;
else return true; end;
end;
fits(arg:SAME):BOOL is
-- will the contents of 'arg' fit into self, ignoring tranposition.
if void(arg) or void(self) or arg.asize /= asize then
return false; else return true end;
end;
is_eq(m: SAME): BOOL pre ~void(self) and ~void(m) and is_same_shape(m) is
loop if elt!/=m.elt! then return false end; end; return true end;
reshape(r,c:INT) pre r*c = asize is
-- Reshape 'self' to have 'r' rows and 'c' columns but keeping
-- actual data, as laid out in one dimension, in the same place.
-- This only changes the bounds, the data will logically move
-- rows and columns keeping column major layout.
-- Example: [a d]
-- m1=[b e]
-- [c f].
-- m1.nr = 3, m1.nc = 2. After m1.reshape(2,3):
-- [a c e]
-- m1=[b d f]
--
-- This feature is NOT in the abstract supertype because it is
-- representation dependent.
nc := c; nr := r;
end;
create(r,c:INT):SAME pre (r>0) and (c>0) is
-- Create a matrix with r rows and c columns
res::=new(r*c);
res.nr := r; res.nc := c;
return(res);
end;
create(arg:SAME):SAME pre ~void(arg) is
-- Creates a new matrix with the same dimensions (but
-- not the same values) as arg
res::=new(arg.asize);
res.nr := arg.nr; res.nc := arg.nc;
return(res);
end;
create(a: ARRAY{ARRAY{ET}}): SAME
-- Create a new array with the same dimensions and values as
-- a, which is an array of arrays(rows).
-- Assume that all the rows of "a" have the same number of elements
pre a.size > 0 and a[0].size > 0 is
sz1 ::= a.size;
sz2 ::= a[0].size;
res ::= #SAME(sz1,sz2);
loop r::=sz1.times!;
loop c::=sz2.times!; res[r,c] := a[r][c]; end end;
return(res);
end;
str:STR is
-- The string form of self represented as a list of rows,
-- eg. "||1.00,2.33|,|4.5,2.8|,|9.7,3.2||".
sc::=#FSTR + "|";
loop r::=nr.times!;
if r/=0 then sc := sc+ "," end;
sc := sc + "|";
loop c::=nc.times!;
if c/=0 then sc := sc+"," end;
sc := sc + [r,c].str; end;
-- #OUT+r+","+c+":"+sc+"\n"; end;
sc := sc+ "|" end;
sc := sc+"|";
return(sc.str) end;
dimension_str:STR is
-- useful for debugging, prints out "nr = self.nr, nc = self.nc"
return "nr = " + self.nr + ", nc = " + self.nc;
end;
copy:SAME is
-- make a value copy.
res ::= create(self);
res.inplace_contents(self);
return(res);
end;
contents(arg:SAME) is inplace_contents(arg) end;
inplace_contents(arg:SAME) pre is_same_shape(arg) is
-- copy the contents of arg into self.
sz ::= asize; i::=0;
loop while!(i<sz); self[i] := arg[i];
i := i+1;
end;
end;
inplace_contents_from_function(function:ROUT{INT,INT}:ET) is
-- Set the contents of the matrix from the function "function"
loop
i1 ::= col_ind!;
loop
i2 ::= row_ind!;
[i1,i2] := function.call(i1,i2);
end;
end;
end;
inplace_portion_of_arg(a: SAME) is
-- Copy into self as much of arg as will fit and return it. Don't
-- alter other elements.
nrows: INT := nr.min(a.nr);
loop r::= nrows.times!;
loop set_row!(r,a.row_elt!(r)) end
end
end;
ident:SAME is
-- Create an identity matrix of the same shape as self
res ::= create(self);
-- here we can assume that entries are already set to 0 via create
d ::= nc.min(nr);
i ::= 0; loop while!(i<d);
res[i,i] := element_one;
i := i+1;
end;
return res;
end;
inplace_ident is
-- Should work for non rectangular matrices too, non
-- square portion will be set to zero.
sz ::= asize; i::=0; r::=0; c::=0; lnr ::= nr;
loop while!(i<sz);
if (r = c) then [i] := element_one; else [i] := element_zero; end;
r := r+1;
if (r >= lnr) then r := 0; c := c+1; end;
i := i+1;
end;
end;
trans:SAME is
-- Create a new matrix that is the transpose of self
lnc ::= nc; res ::= #SAME(lnc,nr);
-- reverse columns and rows return new value.
sz ::= asize; j::=0; i::=0;
loop while!(i<sz);
res[j] := self[i];
j := j+lnc;
diff ::= j-sz; if (diff>=0) then j:=diff+1; end;
i := i+1;
end;
return res;
end;
inplace_trans is
-- make self = transpose of self.
if (nr = nc) then
-- can do it in place!
d ::= nr; sz ::= asize;
i ::= 1; j::=d; c::=0;
-- Count through lower diagonal indexes and corresponding
-- upper diagonal indexes in this non-obvious but efficient
-- manner
k ::= 0; -- diagonal index.
loop
while!(i < sz);
tmp ::= [j]; [j] := [i]; [i] := tmp;
-- flip i and i transpose
i := i+1; j:= j+d;
if j>=sz then
k := k+d+1;
i := k+1; -- one down
j := k+d; -- one to the right
end;
end;
else
tmp ::= self.trans; -- create temporary.
self.nc := tmp.nc;
self.nr := tmp.nr;
self.inplace_contents(tmp);
end;
end;
inplace_arg_trans(arg:SAME) pre fits(arg) is
-- self <- arg^T
lnc ::= arg.nc;
nc := arg.nr;
nr := lnc;
-- reverse columns and rows return new value.
sz ::= asize; j::=0; i::=0;
loop while!(i<sz);
self[j] := arg[i];
j := j+lnc;
diff ::= j-sz; if (diff>=0) then j:=diff+1; end;
i := i+1;
end;
end;
times_elt(s:ET):SAME is
-- Return a new matrix that is self * scalar s
-- Self is unchanged
return(scaled_by(s));
end;
inplace(s:ET) is
-- Set all elements to have the value "s"
sz ::= asize; i::=0;
loop while!(i<sz); [i] := s; i := i+1; end;
end;
inplace_zero is
-- Set all elements to zero
inplace(ET::zero);
end;
scaled_by(s:ET):SAME is
res ::= #SAME(self);
sz ::= asize; i::=0;
loop while!(i<sz); res[i] := s*self[i]; i := i+1; end;
return res;
end;
inplace_scaled_by(s:ET) is
sz ::= asize; i::=0;
loop while!(i<sz); [i] := s*[i]; i := i+1; end;
end;
inplace_elements(s:ET) is
sz ::= asize; i::=0;
loop while!(i<sz); [i] := s; i := i+1; end;
end;
aget(i1,i2:INT):ET pre (i1 >=0) and (i1 <= nr) and (i2>=0) and (i2<=nc) is
-- The element with indices `[i1,i2]'.
return([i1+i2*nr]) end;
aset(i1,i2:INT,val:ET) pre (i1 >=0) and (i1 <= nr) and
(i2>=0) and (i2<=nc) is
-- Set the element with indices `[i1,i2]' to val.
[i1+i2*nr]:=val end;
plus(arg:SAME):SAME is return(plus_arg(arg)); end;
plus_arg(arg:SAME):SAME pre is_same_shape(arg) is
res ::= #SAME(self);
res.inplace_arg_plus_arg(self,arg);
return(res);
end;
minus(arg:SAME):SAME is return(minus_arg(arg)); end;
minus_arg(arg:SAME):SAME pre is_same_shape(arg) is
res ::= #SAME(self);
res.inplace_arg_minus_arg(self,arg);
return(res);
end;
inplace_plus_arg(arg:SAME) pre is_same_shape(arg) is
self.inplace_arg_plus_arg(self,arg);
end;
inplace_minus_arg(arg:SAME) pre is_same_shape(arg) is
self.inplace_arg_minus_arg(self,arg);
end;
plus_arg_trans(arg:SAME):SAME pre is_same_shape_trans(arg) is
res ::= #SAME(self);
res.inplace_arg_plus_arg_trans(self,arg);
return res;
end;
minus_arg_trans(arg:SAME):SAME pre is_same_shape_trans(arg) is
res ::= #SAME(self);
res.inplace_arg_minus_arg_trans(self,arg);
return res;
end;
inplace_plus_arg_trans(arg:SAME) pre is_same_shape_trans(arg) is
self.inplace_arg_plus_arg_trans(self,arg);
end;
inplace_minus_arg_trans(arg:SAME) pre is_same_shape_trans(arg) is
self.inplace_arg_minus_arg_trans(self,arg);
end;
-- Three way operations: self <- arg1^{T} (+/-) arg2{^T}
-- can write others in terms of these.
inplace_arg_plus_arg(arg1,arg2:SAME) pre is_same_shape(arg1) and
arg1.is_same_shape(arg2) is
sz ::= asize; i::=0;
loop while!(i<sz); self[i] := arg1[i] + arg2[i];
i := i+1;
end;
end;
inplace_arg_minus_arg(arg1,arg2:SAME) pre is_same_shape(arg1) and
arg1.is_same_shape(arg2) is
sz ::= asize; i::=0;
loop while!(i<sz); self[i] := arg1[i] - arg2[i];
i := i+1;
end;
end;
inplace_arg_plus_arg_trans(arg1,arg2:SAME) pre fits(arg1)
and arg1.is_same_shape_trans(arg2) and ~SYS::ob_eq(self,arg2) is
lnc ::= arg1.nc;
nc := lnc; nr := arg1.nr;
sz ::= asize; j::=0; i::=0;
loop while!(i<sz);
self[i] := arg1[i] + arg2[j];
j := j+lnc;
diff ::= j-sz; if (diff>=0) then j:=diff+1; end;
i := i+1;
end;
end;
inplace_arg_minus_arg_trans(arg1,arg2:SAME) pre fits(arg1)
and arg1.is_same_shape_trans(arg2) and ~SYS::ob_eq(self,arg2) is
lnc ::= arg1.nc;
nc := lnc; nr := arg1.nr;
sz ::= asize; j::=0; i::=0;
loop while!(i<sz);
self[i] := arg1[i] - arg2[j];
j := j+lnc;
diff ::= j-sz; if (diff>=0) then j:=diff+1; end;
i := i+1;
end;
end;
inplace_arg_trans_plus_arg_trans(arg1,arg2:SAME) pre fits(arg1)
and arg1.is_same_shape(arg2) and
~SYS::ob_eq(self,arg1) and ~SYS::ob_eq(self,arg2) is
lnc ::= arg1.nc;
nr := lnc; nc := arg1.nr;
sz ::= asize; j::=0; i::=0;
loop while!(i<sz);
self[j] := arg1[i] + arg2[i];
j := j+lnc;
diff ::= j-sz; if (diff>=0) then j:=diff+1; end;
i := i+1;
end;
end;
inplace_arg_trans_minus_arg_trans(arg1,arg2:SAME) pre fits(arg1)
and arg1.is_same_shape(arg2) and
~SYS::ob_eq(self,arg1) and ~SYS::ob_eq(self,arg2) is
lnc ::= arg1.nc;
nr := lnc; nc := arg1.nr;
sz ::= asize; j::=0; i::=0;
loop while!(i<sz);
self[j] := arg1[i] - arg2[i];
j := j+lnc;
diff ::= j-sz; if (diff>=0) then j:=diff+1; end;
i := i+1;
end;
end;
-- with scaling, euphemisms for elementary 3 way operations.
plus_scaled_arg(s:ET,arg:SAME):SAME is
res ::= #SAME(self);
res.inplace_arg_plus_scaled_arg(self,s,arg);
return res;
end;
inplace_plus_scaled_arg(s:ET,arg:SAME) is
self.inplace_arg_plus_scaled_arg(self,s,arg);
end;
plus_scaled_arg_trans(s:ET,arg:SAME):SAME is
res ::= #SAME(self);
res.inplace_arg_plus_scaled_arg_trans(self,s,arg);
return(res);
end;
inplace_plus_scaled_arg_trans(s:ET,arg:SAME) is
self.inplace_arg_plus_scaled_arg_trans(self,s,arg);
end;
-- with scaling, 3 way operations.
inplace_arg_plus_scaled_arg(arg1:SAME,s:ET,arg2:SAME) pre
is_same_shape(arg1) and arg1.is_same_shape(arg2) is
sz ::= asize; i::=0;
loop while!(i<sz); self[i] := arg1[i] + s*arg2[i];
i := i+1;
end;
end;
inplace_arg_plus_scaled_arg_trans(arg1:SAME,s:ET,arg2:SAME) pre fits(arg1)
and arg1.is_same_shape_trans(arg2) and ~SYS::ob_eq(self,arg2) is
lnc ::= arg1.nc;
nc := lnc; nr := arg1.nr;
sz ::= asize; j::=0; i::=0;
loop while!(i<sz);
self[i] := arg1[i] + s*arg2[j];
j := j+lnc;
diff ::= j-sz; if (diff>=0) then j:=diff+1; end;
i := i+1;
end;
end;
--
-- Matrix Multiplication.
--
times(arg:SAME):SAME pre nc = arg.nr is
res ::= #SAME(nr,arg.nc);
res.inplace_arg_times_arg(self,arg);
return res;
end;
trans_times_arg(arg:SAME):SAME pre nr = arg.nr is
res ::= #SAME(nc,arg.nc);
res.inplace_arg_trans_times_arg(self,arg);
return res;
end;
times_arg_trans(arg:SAME):SAME pre nc = arg.nc is
res ::= #SAME(nr,arg.nr);
res.inplace_arg_times_arg_trans(self,arg);
return res;
end;
trans_times_arg_trans(arg:SAME):SAME pre nr = arg.nc is
res ::= #SAME(nc,arg.nr);
res.inplace_arg_trans_times_arg_trans(self,arg);
return res;
end;
-- in place versions:
inplace_arg_times_arg(arg1,arg2:SAME) pre nr = arg1.nr and
nc = arg2.nc and arg1.nc = arg2.nr is
-- self := arg1 * arg2.
-- For all i,j, self[i,j] = sum(k) arg1[i,k] * arg2[k,j]
selfnr ::= nr; selfnc ::= nc;
a1nr ::= arg1.nr; a1nc ::= arg1.nc;
a2nr ::= arg2.nr; a2nc ::= arg2.nc;
-- This is what you need to do to get reasonable performance on
-- matrix codes: take out of the loop the array index multiplication,
-- i.e. the definition of [i,j] -> [i+j*nr]
-- Eventually the compiler will figure this out. Note that
-- this is not as trivial as you might disdainfully think, as in
-- Sather, and unlike Fortran, the size of the array could conceivably
-- change during a loop through any call that might modify nr or nc.
-- The array reference itself might change as well. Sather 1.0.8 and
-- above ought to have dataflow optimizations that can do this in
-- most cases, but I'd rather be safe than sorry here.
--
-- The following routine seems to generate assembly code identical
-- to a naive C language implementation for its inner loops with GNU
-- CC for ET=FLT.
i::=0;
loop while!(i<selfnr);
j::=0;
loop while!(j<selfnc);
sum ::= element_zero;
k::=0;
loop while!(k < a1nc);
sum := sum+arg1[i+k*a1nr]*arg2[k+j*a2nr]; k := k+1;
end;
[i+j*selfnr] := sum;
j := j+1;
end;
i := i+1;
end;
end;
inplace_arg_times_arg_fast(arg1,arg2:SAME)
-- self := arg1 * arg2.
-- Basic algorithm:
-- For all i,j, self(i,j) = sum(k) arg1[i,k] * arg2[k,j]
-- t1 = i+j*nr, index through "self". 0 .. asize-1
-- t2 = i+arg1.nr*k 0 .. arg1.asize-1
-- t3 = k+j*arg2.nr
pre nr = arg1.nr and nc = arg2.nc and arg1.nc = arg2.nr is
lnr ::= nr; lnc ::= nc;
sz ::= asize; szarg1 ::= arg1.asize;
lk ::= arg1.nc; -- lk is the bounds of the innermost loop.
j::=0;
idx0 ::= 0;
idx2init ::= 0;
loop while!(j<lnc);
i::=0;
loop while!(i<lnr);
sum ::= element_zero;
idx1 ::= i;
idx2 ::= idx2init; -- was "j*lk"
assert(idx2init = j*lk);
k::=lk-1; -- do innermost loop lk times.
loop while!(k >= 0);
sum := sum+arg1[idx1]*arg2[idx2];
idx1 := idx1+lnr; -- lnr is = arg1.nr
idx2 := idx2+1;
k := k-1;
end;
-- [i+j*nr] := sum;
[idx0] := sum;
assert (i+j*lnr = idx0);
idx0 := idx0+1;
i := i+1;
end;
j := j+1;
idx2init := idx2init + lk;
end;
end;
inplace_arg_trans_times_arg(arg1,arg2:SAME)
-- self := arg1^T * arg2.
-- For all i,j, self[i,j] = sum(k) arg1[k,i] * arg2[k,j]
-- Likely faster than arg * arg, as inner loop is unit stride
-- for both arrays.
pre nr = arg1.nc and nc = arg2.nc and arg1.nr = arg2.nr
is
selfnr ::= nr; selfnc ::= nc;
a1nr ::= arg1.nr; a1nc ::= arg1.nc;
a2nr ::= arg2.nr; a2nc ::= arg2.nc;
i::=0;
loop while!(i<selfnr);
j::=0;
loop while!(j<selfnc);
sum ::= element_zero;
k::=0;
loop while!(k < a1nr);
sum := sum+arg1[k+i*a1nr]*arg2[k+j*a2nr]; k := k+1;
end;
[i+j*selfnr] := sum;
j := j+1;
end;
i := i+1;
end;
end;
inplace_arg_times_arg_trans(arg1,arg2:SAME)
-- self := arg1 * arg2^T.
-- For all i,j, self[i,j] = sum(k) arg1[i,k] * arg2[j,k]
pre nr = arg1.nr and nc = arg2.nr and arg1.nc = arg2.nc
is
selfnr ::= nr; selfnc ::= nc;
a1nr ::= arg1.nr; a1nc ::= arg1.nc;
a2nr ::= arg2.nr; a2nc ::= arg2.nc;
i::=0;
loop while!(i<selfnr);
j::=0;
loop while!(j<selfnc);
sum ::= element_zero;
k::=0;
loop while!(k < a1nc); -- would be better to rearrange loop
sum := sum+arg1[i+k*a1nr]*arg2[j+k*a2nr]; k := k+1;
end;
[i+j*selfnr] := sum;
j := j+1;
end;
i := i+1;
end;
end;
inplace_arg_trans_times_arg_trans(arg1,arg2:SAME)
-- self := arg1^T * arg2^T.
-- For all i,j, self[i,j] = sum(k) arg1[k,i] * arg2[j,k]
pre nr = arg1.nc and nc = arg2.nr and arg1.nr = arg2.nc
is
selfnr ::= nr; selfnc ::= nc;
a1nr ::= arg1.nr; a1nc ::= arg1.nc;
a2nr ::= arg2.nr; a2nc ::= arg2.nc;
i::=0;
loop while!(i<selfnr);
j::=0;
loop while!(j<selfnc);
sum ::= element_zero;
k::=0;
loop while!(k < a1nr);
sum := sum+arg1[k+i*a1nr]*arg2[j+k*a2nr]; k := k+1;
end;
[i+j*selfnr] := sum;
j := j+1;
end;
i := i+1;
end;
end;
inplace_times_diagonal(v1:VT) is
-- Set self to be the product of itself with a diagonal matrix whose
-- diagonal entries are the components of v1 truncated or extended
-- with zeroes to be the correct size.
-- Scale the columns of self with the elements of v1
-- Self <- Self*v1 (as diagonal of a matrix)
loop c::=nc.min(v1.dim).times!;
loop set_col!(c,v1[c]*col_elt!(c)) end
end;
loop c::=v1.dim.upto!(nc-1);
loop set_col!(c,ET::zero) end;
end;
end;
--
-- MATRIX/VECTOR operations
--
-- create a new argument
times_vec(arg:VT):VT pre arg.dim = self.nc is
-- This is a syntactical sugar expressions (m*v) so it
-- doesn't follow the naming convention.
res:VT := #VT(nr);
times_vec_into_vec(arg,res);
return res;
end;
trans_times_vec(arg:VT):VT pre arg.dim = self.nr is
res:VT := #VT(nc);
trans_times_vec_into_vec(arg,res);
return res;
end;
-- in place
times_vec_into_vec(arg,dest:VT)
pre arg.dim = self.nc and dest.dim = self.nr
is
selfnr ::= nr; selfnc ::= nc;
i ::= 0;
loop while!(i<selfnr);
sum ::= element_zero;
j::= 0;
loop while!(j<selfnc);
sum := sum + self[i,j]*arg[j];
j := j+1;
end;
dest[i] := sum;
i:=i+1;
end;
end;
trans_times_vec_into_vec(arg,dest:VT)
pre arg.dim = self.nr and dest.dim = self.nc
is
selfnr ::= nr; selfnc ::= nc;
i ::= 0;
loop while!(i<selfnc);
sum ::= element_zero;
j::= 0;
loop while!(j<selfnr);
sum := sum + self[j,i]*arg[j];
j := j+1;
end;
dest[i] := sum;
i:=i+1;
end;
end;
times_scaled_vec_into_vec(s:ET,arg,dest:VT)
pre arg.dim = self.nc and dest.dim = self.nr
is
selfnr ::= nr; selfnc ::= nc;
i ::= 0;
loop while!(i<selfnr);
sum ::= element_zero;
j::= 0;
loop while!(j<selfnc);
sum := sum + self[i,j]*arg[j];
j := j+1;
end;
dest[i] := s*sum;
i:=i+1;
end;
end;
trans_times_scaled_vec_into_vec(s:ET,arg,dest:VT)
pre arg.dim = self.nr and dest.dim = self.nc
is
selfnr ::= nr; selfnc ::= nc;
i ::= 0;
loop while!(i<selfnc);
sum ::= element_zero;
j::= 0;
loop while!(j<selfnr);
sum := sum + self[j,i]*arg[j];
j := j+1;
end;
dest[i] := s*sum;
i:=i+1;
end;
end;
inplace_plus_scaled_vec_times_vec(s:ET,v1,v2:VT)
-- self := self + s*v1*v2^T
-- (Add scaled outer product of v1 and v2 to self. A BLAS operation)
--
pre v1.dim = nr and v2.dim = nc
is
selfnr ::= nr; selfnc ::= nc;
i ::= 0;
loop while!(i<selfnc);
j::= 0;
loop while!(j<selfnr);
[j,i] := [j,i] +s*v1[j]*v2[i];
j := j+1;
end;
i:=i+1;
end;
end;
--
-- MATRIX/VECTOR MANIPULATION
--
create_col_matrix(arg:VT):SAME is
d ::= arg.dim;
res ::= create(d,1);
i ::= 0; loop while!(i<d); res[i] := arg[i]; i:=i+1; end;
end;
create_row_matrix(arg:VT):SAME is
d ::= arg.dim;
res ::= create(1,d);
i ::= 0; loop while!(i<d); res[i] := arg[i]; i := i+1; end;
end;
col(i:INT):VT is
selfnr ::= nr;
res:VT := #VT(selfnr);
j::=0; loop while!(j<selfnr); res[j] := [j,i]; j := j+1; end;
end;
row(i:INT):VT is
selfnc ::= nc;
res:VT := #VT(selfnc);
j::=0; loop while!(j<selfnc); res[j] := [i,j]; j := j+1; end;
end;
col(i:INT,v:VT) pre v.dim = nr is
d ::= v.dim;
j ::= 0;
loop while!(j < d); [j,i] := v[j]; j:= j+1; end;
end;
row(i:INT,v:VT) pre v.dim = nc is
d ::= v.dim;
j ::= 0;
loop while!(j < d); [i,j] := v[j]; j:= j+1; end;
end;
inplace_scaled_col(s:ET,i:INT) pre i.is_bet(0,nc-1) is
d ::= nr; j ::= 0;
loop while!(j < d); [j,i] := [j,i]*s; j:= j+1; end;
end;
inplace_scaled_row(s:ET,i:INT) pre i.is_bet(0,nr-1) is
d ::= nc; j ::= 0;
loop while!(j < d); [i,j] := [i,j]*s; j:= j+1; end;
end;
inplace_col_plus_scaled_vec(i:INT,s:ET,v:VT)
pre i.is_bet(0,nc-1) and v.dim = nr
is
d ::= nr; j ::= 0;
loop while!(j < d); [j,i] := [j,i] + s*v[j]; j:= j+1; end;
end;
inplace_row(i: INT,v: VT) pre i.is_bet(0,nr-1) and v.dim = nc
is
d ::= nc; j ::= 0;
loop while!(j < d); [i,j] := v[j]; j:= j+1; end;
end;
inplace_row_plus_scaled_vec(i:INT,s:ET,v:VT)
pre i.is_bet(0,nr-1) and v.dim = nc is
d ::= nc; j ::= 0;
loop while!(j < d); [i,j] := [i,j] + s*v[j]; j:= j+1; end;
end;
inplace_swapped_col(i:INT,v:VT) pre i.is_bet(0,nc-1) and v.dim = nr is
d ::= nr; j ::= 0;
loop while!(j < d); t ::= [j,i]; [j,i] := v[j]; v[j] := t; j:= j+1; end;
end;
inplace_swapped_row(i:INT,v:VT) pre i.is_bet(0,nr-1) and v.dim = nc is
d ::= nc; j ::= 0;
loop while!(j < d); t ::= [i,j]; [i,j] := v[j]; v[j] := t; j:= j+1; end;
end;
submatrix(lr,ur:INT, lc,uc:INT):SAME pre 0 <= lr and lr <= ur and
ur < nr and 0 <= lc and lc <= uc and uc < nr is
-- return a submatrix
rnr ::= ur-lr+1; rnc ::= uc-lc+1;
res ::= #SAME(rnr,rnc);
j::=0;
loop
while!(j<rnc);
i ::= 0;
loop
while!(i<rnr); res[i,j] := [i+lr,j+lc];
i := i+1;
end;
j := j+1;
end;
return res;
end;
inplace_submatrix_to_arg(lr,ur:INT, lc,uc:INT,arg:SAME)
-- Set the submatrix of self given by [lr..ur,lc..uc] to be arg.
-- with proposed syntax extension:
-- m.submatrix(1,3,2,4) := m2;
pre 0 <= lr and lr <= ur and ur < nr and 0 <= lc
and lc <= uc and uc < nr and arg.nr = ur-nr+1 and arg.nc = uc-lc+1
is
anr ::= arg.nr; anc ::= arg.nc;
j::=0;
loop
while!(j<anc);
i ::= 0;
loop
while!(i<anr); [i+lr,j+lc] := arg[i,j];
i := i+1;
end;
j := j+1;
end;
end;
inplace_swapped(arg:SAME) pre is_same_shape(arg) is
-- Swap the elements of "arg" with self
sz ::= asize;
i ::= 0;loop while!(i<sz); t ::= arg[i]; arg[i] := [i]; [i] := t; i:=i+1; end;
end;
ind1!: INT is
-- Yield each value of the first index in order. The rows
loop yield(size1.times!); end
end;
ind2!:INT is
-- Yield each value of the second index in order. The columns
loop yield(size2.times!); end
end;
row_ind!: INT is
-- Yield each value of the first index in order. The rows
loop yield(size1.times!); end
end;
col_ind!:INT is
-- Yield each value of the second index in order. The columns
loop yield(size2.times!); end
end;
inds!:TUP{INT,INT} is
-- Yield tuples of the indices of self in lexicographical order.
loop row ::=size1.times!;
loop yield(#TUP{INT,INT}(row,size2.times!)); end
end
end;
elt!: ET pre ~void(self) is
-- Yield all elements in row major order
loop yield(aelt!) end;
end;
inplace_elt!(val:ET) is
-- Set all elements in row major order
loop aset!(val); yield; end
end;
row_elt!(once row:INT):ET pre ~void(self) is
-- Yield elements by varying index 2 and holding index 1 at `row'.
-- The elements of a row "row"
loop yield(aelt!(row,size2,size1)); end
end;
col_elt!(once col:INT):ET pre ~void(self) is
-- Yield elements by varying index 1 and holding index 2 at `col'.
-- The elements of a "column" col
loop yield(aelt!(col*size1,size1,1)); end
end;
set_row!(once i1:INT, val:ET) pre ~void(self) is
-- Set to val elements with varying index 2 and index 1 fixed at `i1'.
-- i.e. setting the row i1
loop aset!(i1,size2,size1,val); yield end
end;
set_col!(once i2:INT, val:ET) pre ~void(self) is
-- Set to val elements with varying index 1 and index 2 fixed at `i2'.
-- i.e. setting the column i2
loop aset!(i2*size1,size1,val); yield end
end;
inplace_row!(once row:INT, val:ET) pre ~void(self) is
-- Set to val elements with varying index 2 and index 1 fixed at `row'.
-- i.e. setting a row "row"
loop aset!(row,size2,size1,val); yield end end;
inplace_col!(once col:INT, val:ET) pre ~void(self) is
-- Set to val elements with varying index 1 and index 2 fixed at `col'.
-- i.e. setting the column col
loop aset!(col*size1,size1,val); yield end
end;
diag_elt!: ET pre ~void(self) is
-- Yield values along the diagonal (square in smaller dimension)
loop ind ::= (size1.min(size2)).times!; yield([ind,ind]) end;
end;
inplace_diag_elt!(val:ET) pre ~void(self) is
-- Set values along the diagonal (square in smaller dimension)
loop id ::= (size1.min(size2)).times!; [id,id] := val; yield; end;
end;
elt1!(once i1:INT):ET pre ~void(self) is
-- Yield elements by varying index 2 and holding index 1 at `i1'.
-- The elements of a row "i1"
-- this is the same as row_elt!
loop yield(aelt!(i1,size2,size1)); end
end;
elt2!(once i2:INT):ET pre ~void(self) is
-- Yield elements by varying index 1 and holding index 2 at `i2'.
-- The elements of a "column" i2
-- this is the same as col_elt!
loop yield(aelt!(i2*size1,size1,1)); end
end;
set1!(once i1:INT, val:ET) pre ~void(self) is
-- Set to val elements with varying index 2 and index 1 fixed at `i1'.
-- i.e. setting the row i1
-- this is the same as set_row!
loop aset!(i1,size2,size1,val); yield end
end;
set2!(once i2:INT, val:ET) pre ~void(self) is
-- Set to val elements with varying index 1 and index 2 fixed at `i2'.
-- i.e. setting the column i2
-- this is the same as set_col!
loop aset!(i2*size1,size1,val); yield end
end;
end;
-----------------------------------------------------------------------
class NUMERIC_MAT{ET<$NUMBER{ET},VT<$VEC{ET,VT}} < $MAT{ET,VT,NUMERIC_MAT{ET,VT}} is
-- Functions that don't work on complex numbers but do work on
-- reals
include MAT{ET,VT};
destructive_invert: SAME pre nr=nc is
-- Destructive invert self, return a new matrix. Similar to the
-- Gaussian algorithm. Raise "Division by Zero" for singular
-- matrices. The Gaussian algorithm is from Sedgewick:
-- "Algorithms", pp 57-65.
-- eliminate
A::=create(nr,nr);
A.inplace_ident;
loop i::=0.upto!(nr-1);
max::=i;
loop j::=(i+1).upto!(nr-1);
if [j,i].abs > [max,i].abs then max:=j end;
end; -- loop
loop k::=i.upto!(nr-1);
t::=[i,k]; [i,k]:=[max,k]; [max,k]:=t;
end; -- loop
loop
t::=A.row_elt!(i);
A.set_row!(i,A.row_elt!(max));
A.set_row!(max,t);
end; -- loop
loop j::=(i+1).upto!(nr-1);
loop
A.set_row!(j, A.row_elt!(j) - A.row_elt!(i)*[j,i]/[i,i]);
end; -- loop
loop k::=(nr-1).downto!(i);
[j,k] := [j,k] - [i,k]*[j,i]/[i,i];
end; -- loop
end; -- loop
end; -- loop
-- substitute
loop j::=(nr-1).downto!(0);
loop col::=A.col_ind!;
t::=ET::zero;
loop k::=(j+1).upto!(nr-1);
t := t + [j,k]*A[col,k];
end; -- loop
A[j,col] := (A[j,col]-t)/[j,j];
end; -- loop
end; -- loop
return A
end; -- ninv
invert: SAME pre nr=nc is return copy.destructive_invert end;
-- Non-destructive inversion
destructive_det: ET pre nr=nc is
-- Destructive determinant of self. Similar to the Gaussian algorithm.
-- The Gaussian algorithm is from Sedgewick: "Algorithms", pp 57-65.
-- eliminate
loop i::=0.upto!(nr-1);
max::=i;
loop j::=(i+1).upto!(nr-1);
if [j,i].abs > [max,i].abs then max:=j end;
end; -- loop
loop k::=i.upto!(nr-1);
t::=[i,k]; [i,k]:=[max,k]; [max,k]:=t;
end; -- loop
loop j::=(i+1).upto!(nr-1);
loop k::=(nr-1).downto!(i);
[j,k] := [j,k] - [i,k]*[j,i]/[i,i];
end; -- loop
end; -- loop
end; -- loop
res::=ET::one; loop res:=res*diag_elt!; end;
return res
end; -- destructive_det
det: ET pre nr=nc is return copy.destructive_det end;
-- Non destructive determinant routine
destructive_gauss(x:VT) pre nc=nr and x.dim=nc is
-- Solve linear equations. Result in x. self is changed.
-- Raise "Division by Zero" for singular matrices.
-- This algorithm is from Sedgewick: "Algorithms", pp 57-65.
-- eliminate
loop i::=0.upto!(nc-1);
max::=i;
loop j::=(i+1).upto!(nc-1);
if [j,i].abs > [max,i].abs then max:=j end;
end; -- loop
loop k::=i.upto!(nc-1);
t::=[i,k]; [i,k]:=[max,k]; [max,k]:=t;
end; -- loop
t::=x[i]; x[i]:=x[max]; x[max]:=t;
loop j::=(i+1).upto!(nc-1);
x[j] := x[j] - x[i]*[j,i]/[i,i];
loop k::=(nc-1).downto!(i);
[j,k] := [j,k] - [i,k]*[j,i]/[i,i];
end; -- loop
end; -- loop
end; -- loop
-- substitute
loop j::=(nc-1).downto!(0);
t::=ET::zero;
loop k::=(j+1).upto!(nc-1);
t := t + [j,k]*x[k];
end; -- loop
x[j] := (x[j]-t)/[j,j];
end; -- loop
end; -- destructive_gauss
gauss(x:VT):VT pre nc=nr and x.dim=nc is
-- Non destructive gaussian routine
y::=x.copy; copy.destructive_gauss(y); return y
end;
end;
----------------------------------------------------------------------------
class MAT < $MAT{FLT,VEC,MAT} is
-- Includes some functions that only work with FLTs
-- Generalizing these functions is possible, but would require definitions
-- of machine epsilon in the numeric classes
include NUMERIC_MAT{FLT,VEC};
inplace_uniform_random is
-- Become self's entries uniform in `[0.,1.)'
loop r::=row_ind!; loop [r,col_ind!] := RND::uniform.flt; end; end;
end; -- inplace_uniform_random
svd_in(a:MAT, w:VEC, v:MAT)
-- Computes the singular value decomposition of `self = a w v^T'.
-- `a' must be `max(nr,nc)' by `nc', `w' length `nc', `v' is `nc' by
-- `nc'. `Self' is unchanged, `a', `w', `v' are altered.
pre a.nr=nr.max(nc) and a.nc=nc and
w.dim=nc and v.nr=nc and v.nc=nc is
-- fill in a with self and extra zero rows if necessary
a.inplace_zero; a.inplace_portion_of_arg(self);
NR_SVD::svd(a,w,v); -- Start with Numerical Recipes version.
-- Eventually use a better algorithm.
end; -- svd_in
svd_back_sub(u:MAT, w:VEC, v:MAT, b,x:VEC)
-- Solves `a.x=b' for `x' when `a=u.d.v^T' is the svd of `a'.
pre u.nc=w.dim and v.nr=u.nc and v.nc=u.nc and
b.dim=u.nr and x.dim=u.nc is
tmp::=#VEC(u.nc);
j:INT; loop until!(j=u.nc); -- calculate u^T.b in tmp
s:FLT:=0.0;
if w[j].abs>=0.000001 then -- nonzero only if w_j is nonzero
i:INT:=0; loop until!(i=u.nr);
s:=s+u[i,j]*b[i]; i:=i+1;
end; -- loop
s:=s/w[j];
end; -- if
tmp[j]:=s;
j:=j+1;
end; -- loop
j:=0; loop until!(j=u.nc);
s:FLT:=0.0;
jj:INT:=0; loop until!(jj=u.nc);
s:=s+v[j,jj]*tmp[jj]; jj:=jj+1
end; -- loop
x[j]:=s;
j:=j+1;
end; -- loop
end; -- svd_back_sub
inplace_linear_fit_of(vin,vout:ARRAY{VEC}):MAT
-- Fill vin `self' to be the least squares best linear approximation
-- relating `vin' to `vout' by: `out[i]=self.act_on(in[i])'.
-- Return `self'.
pre nr=vout[0].dim and nc=vin[0].dim and vout.size=vin.size is
it:MAT:=create(vin.size,vin[0].dim);
i:INT; loop until!(i=vin.size); it.inplace_row(i,vin[i]); i:=i+1; end;
u:MAT:=create(it.nr.max(it.nc),it.nc);
v:MAT:=create(it.nc,it.nc); w:VEC:=w.create(it.nc);
it.svd_in(u,w,v);
wmax:FLT:=w.max_value; wmin:FLT:=wmax*(0.000001);
i:=0; loop until!(i=it.nc);
if w[i]<=wmin then w[i]:=0.0 end;
i:=i+1;
end; -- loop
x:VEC:=x.create(nc); b:VEC:=b.create(vout.size);
i:=0; loop until!(i=vout[0].dim); -- get each row of self
j:INT:=0; loop until!(j=vout.size); b[j]:=vout[j][i]; j:=j+1 end;
it.svd_back_sub(u,w,v,b,x);
inplace_row(i,x);
i:=i+1;
end; -- loop
return self;
end; -- inplace_linear_fit_of
inplace_affine_fit_of(vin,vout:ARRAY{VEC})
-- Fill vin `self' to be the best least squares affine map relating
-- `in' to `out' by: `out[i]=self.affine_act_on(vin[i])'.
pre nr=vout[0].dim and nc=vin[0].dim+1 and vout.size=vin.size is
it:MAT:=create(vin.size,vin[0].dim+1);
i:INT; loop until!(i=vin.size);
it.inplace_row(i,vin[i]); it[i,vin[0].dim]:=1.0; -- put in affine piece
i:=i+1;
end;
u:MAT:=create(it.nr.max(it.nc),it.nc);
v:MAT:=create(it.nc,it.nc); w:VEC:=w.create(it.nc);
it.svd_in(u,w,v);
wmax:FLT:=w.max_value; wmin:FLT:=wmax*(0.000001);
i:=0; loop until!(i=it.nc);
if w[i]<=wmin then w[i]:=0.0 end;
i:=i+1;
end; -- loop
x:VEC:=x.create(nc); b:VEC:=b.create(vout.size);
i:=0; loop until!(i=vout[0].dim); -- get each row of self
j:INT:=0; loop until!(j=vout.size); b[j]:=vout[j][i]; j:=j+1 end;
it.svd_back_sub(u,w,v,b,x);
inplace_row(i,x);
i:=i+1;
end; -- loop
end; -- inplace_linear_fit_of
inplace_weighted_linear_fit_of(vin,vout:ARRAY{VEC}, wt:ARRAY{FLT})
-- Fill in `self' to be the least squares best linear approximation
-- relating `vin' to `vout' by: `vout[i]=self.act_on(vin[i])'. `wt[i]'
-- gives the weight which should be given to the ith example.
-- (typically in `[0.,1.]' (`0.' means ignore, `1.' means full weight).
pre
nr=vout[0].dim and nc=vin[0].dim and vout.size=vin.size
and wt.size=vin.size
is
it:MAT:=create(vin.size,vin[0].dim);
i:INT; loop until!(i=it.nr); it.inplace_row(i,vin[i]); i:=i+1; end;
i:=0; loop until!(i=it.nr); -- scale by wt
j:INT:=0; loop until!(j=it.nc); it[i,j]:=it[i,j]*wt[i]; j:=j+1 end;
i:=i+1;
end; -- loop
u:MAT:=create(it.nr.max(it.nc),it.nc);
v:MAT:=create(it.nc,it.nc); w:VEC:=w.create(it.nc);
it.svd_in(u,w,v);
wmax:FLT:=w.max_value; wmin:FLT:=wmax*(0.000001);
i:=0; loop until!(i=it.nc);
if w[i]<=wmin then w[i]:=0.0 end;
i:=i+1;
end; -- loop
x:VEC:=x.create(nc); b:VEC:=b.create(vout.size);
i:=0; loop until!(i=vout[0].dim); -- get each row of self
j:INT:=0; loop until!(j=vout.size); b[j]:=vout[j][i]*wt[j]; j:=j+1 end;
it.svd_back_sub(u,w,v,b,x);
inplace_row(i,x);
i:=i+1;
end; -- loop
end; -- inplace_weighted_linear_fit_of
inplace_weighted_affine_fit_of(vin,vout:ARRAY{VEC}, wt:ARRAY{FLT})
pre
nr=vout[0].dim and nc=vin[0].dim+1 and vout.size=vin.size
and wt.size=vin.size
is
-- Fill in `self' to be the least squares best affine approximation
-- relating `in' to `vout' by: `vout[i]=self.affine_act_on(in[i])'.
-- `wt[i]' gives the weight which should be given to the `i'th example.
-- (typically in `[0.,1.]' (`0.' means ignore, `1.' means full weight).
it:MAT:=create(vin.size,vin[0].dim+1);
i:INT; loop until!(i=it.nr);
it.inplace_row(i,vin[i]); it[i,vin[0].dim]:=wt[i];
i:=i+1;
end; -- loop
i:=0; loop until!(i=it.nr); -- scale by wt
j:INT:=0; loop until!(j=it.nc-1); it[i,j]:=it[i,j]*wt[i]; j:=j+1 end;
i:=i+1;
end; -- loop
u:MAT:=create(it.nr.max(it.nc),it.nc);
v:MAT:=create(it.nc,it.nc); w:VEC:=w.create(it.nc);
it.svd_in(u,w,v);
wmax:FLT:=w.max_value; wmin:FLT:=wmax*(0.000001);
i:=0; loop until!(i=it.nc);
if w[i]<=wmin then w[i]:=0.0 end;
i:=i+1;
end; -- loop
x:VEC:=x.create(nc); b:VEC:=b.create(vout.size);
i:=0; loop until!(i=vout[0].dim); -- get each row of self
j:INT:=0; loop until!(j=vout.size); b[j]:=vout[j][i]*wt[j]; j:=j+1 end;
it.svd_back_sub(u,w,v,b,x);
inplace_row(i,x);
i:=i+1;
end; -- loop
end; -- inplace_weighted_affine_fit_of
end;
-----------------------------------------------------------------------